//自定义
#include "task.hpp"

//c
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <ctime>

//sys
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>

//cc
#include <iostream>
#include <string>
#include <vector>

//宏
#define SubProcessNum 5
#define RBUFFERSIZE 1024
#define EXECTASKTIMES 5

//全局变量
std::vector<task>  TaskVector;

void Menu()
{
    std::cout << "################################################" << std::endl;
    std::cout << "# 0. 吃饭                              1. 睡觉 #" << std::endl;
    std::cout << "# 2. 学习                              3. 运动 #" << std::endl;
    std::cout << "# 4. 唱歌                              5. 退出 #" << std::endl;
    std::cout << "################################################" << std::endl;
}

struct channel
{
    channel(int cmdfd, int subprocessid, const std::string &processname)
    :_cmdfd(cmdfd), _subprocessid(subprocessid), _processname(processname)
    {}

    int _cmdfd;               // 发送任务的文件描述符
    pid_t _subprocessid;      // 子进程的PID
    std::string _processname; // 子进程的名字 -- 方便我们打印日志
    // int _cmdcnt;
};

// 5个子进程跑这份代码
void subProcessExec()
{
    while(true)
    {
        int cmdCodeReceiver = 0;
        ssize_t n = read(0,&cmdCodeReceiver,sizeof(cmdCodeReceiver));
        // std:: cout << n <<std::endl;
        // std::cout<<cmdCodeReceiver<<std::endl;
        // std::cout<<TaskVector[cmdCodeReceiver];
        if(n > 0)
        {
            int pid= getpid();
            std::cout<<"进程"<<pid<<"去";
            TaskVector[cmdCodeReceiver]();
        }
        else if(n == 0)
        {
            int pid= getpid();
            std::cout<<pid<<"做完了"<<std::endl;
            break;
        }
    }
}


void InitSubProcess(std::vector<channel>* channels)
{
    std::vector<int> oldfds; 
    for(int i = 0; i<SubProcessNum; i++)
    {
        int pipefd[2]={0};
        int pipeRet = pipe(pipefd);
        oldfds.push_back(pipefd[1]);
        // for(auto fd:oldfds)
        // {
        //     std::cout<<fd;
        // }
        // std::cout<<std::endl;
        pid_t id = fork();
        assert(pipeRet == 0);
        (void)pipeRet;
        if(id == 0)
        {
            for(auto fd:oldfds)
            {
                close(fd);
            }
            // std::cout<<pipefd[0]<<std::endl;
            // close(pipefd[1]);
            dup2(pipefd[0],0);
            close(pipefd[0]);
            subProcessExec();//子进程执行任务
            exit(0);
        }
        close(pipefd[0]);
        std::string pname = std::to_string(id) + "进程" + std::to_string(i);
        channels->push_back(channel(pipefd[1],id,pname));
    }
}


// 输入：const &
// 输出：*
// 输入输出：&
void AppointSubProcess(const std::vector<channel>& Channels)    
{                   
    int which = 0;//channls下标 
    // for(int i = 0;i<EXECTASKTIMES;i++)
    while(true)
    {
        Menu();
        int selectCode = -1;
        std::cout<<"选择任务:";
        std::cin>>selectCode;
        //选择任务
        /*version1:随机分配任务*/
        // int cmdcode = rand() % TaskVector.size();

        /*随机选择进程*/
        // int subprocessIndex = rand() % Channels.size();

        /*version2:按顺序分配任务:直接使用which*/
        /*按顺序选择进程并按顺序发送任务*/
        if(selectCode < 0 || selectCode >= TaskVector.size() + 1)
        {
            std::cout<<"选择错误，重新选择"<<std::endl;
            continue;
        }
        if(selectCode == 5)
        {
            break;
        }
        write(Channels[which]._cmdfd,&selectCode,sizeof(selectCode));
        // printf("%d\n",Channels[which]._cmdfd);
        which++;
        which %= Channels.size();
        
        sleep(1);
    }
}

void QuitProcess(const std::vector<channel>& Channels)
{
    //为什么这样写可以？？
    // for(auto& c : Channels)
    // {
    //     close(c._cmdfd);
    // }

    // for(auto& c : Channels)
    // {
    //     waitpid(c._subprocessid,nullptr,0);
    // }

    // 为什么这样写会导致bug?
    for(auto& c : Channels)
    {
        close(c._cmdfd);
        waitpid(c._subprocessid,nullptr,0);
    }
}

int main()
{
    loadTask(&TaskVector);
    //随机种子
    srand(time(nullptr)^getpid()^1024);
    std::vector<channel> channels;//存放管道与进程信息
    //1.初始化
    InitSubProcess(&channels);
    // 2. 开始控制子进程
    AppointSubProcess(channels);
    // 3. 回收make子进程
    QuitProcess(channels); 
    return 0;
}